/*
 * Copyright (c) 2024 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import React, { Component, ReactNode } from 'react';
import Orientation from 'react-native-orientation';
import { View, StyleSheet, Dimensions } from 'react-native';
import { Fold } from '../';
import { SMALL_SCREEN_WIDTH } from '../utils/FoldUtils';

export interface FoldSplitContainerProps {
  primary?: ReactNode;
  secondary?: ReactNode;
  extra?: ReactNode;
  expandedLayoutOptions?: ExpandedRegionLayoutOptions;
  hoverModeLayoutOptions?: HoverModeRegionLayoutOptions;
  foldedLayoutOptions?: FoldedRegionLayoutOptions;
  onHoverStatusChange?: OnHoverStatusChangeHandler;
}

interface FoldSplitContainerState {
  primarySize: Size;
  secondSize: Size;
  extraSize: Size;
  primaryPosition: Position;
  secondPosition: Position;
  extraPosition: Position;
}

export interface ExpandedRegionLayoutOptions {
  horizontalSplitRatio?: number;
  verticalSplitRatio?: number;
  isExtraRegionPerpendicular?: boolean;
  extraRegionPosition?: ExtraRegionPosition;
}

export interface HoverModeRegionLayoutOptions {
  horizontalSplitRatio?: number;
  showExtraRegion?: boolean;
  extraRegionPosition?: ExtraRegionPosition;
}

export interface FoldedRegionLayoutOptions {
  verticalSplitRatio: number;
}

export interface HoverModeStatus {
  foldStatus: number;
  isHoverMode: boolean;
}

interface RegionLayout {
  primary: Layout;
  secondary: Layout;
  extra: Layout;
}

interface Position {
  x: number;
  y: number;
}

interface Size {
  width: number;
  height: number;
}

export type OnHoverStatusChangeHandler = (status: HoverModeStatus) => void;

export interface Layout {
  size: Size;
  position: Position;
}

export enum ExtraRegionPosition {
  TOP = 1,
  BOTTOM = 2,
}

export enum PresetSplitRatio {
  LAYOUT_1V1 = 1 / 1,
  LAYOUT_2V3 = 2 / 3,
  LAYOUT_3V2 = 3 / 2,
}

export enum FoldStatus {
  FOLD_STATUS_UNKNOWN = 0,
  FOLD_STATUS_EXPANDED = 1,
  FOLD_STATUS_FOLDED = 2,
  FOLD_STATUS_HALF_FOLDED = 3,
}

export function initLayout(): Layout {
  return {
    size: { width: 0, height: 0 },
    position: { x: 0, y: 0 },
  };
}

export class FoldSplitContainer extends Component<FoldSplitContainerProps, FoldSplitContainerState> {
  private foldStatus: number = FoldStatus.FOLD_STATUS_UNKNOWN;
  private containerSize: Size = { width: 0, height: 0 };
  private containerGlobalPosition: Position = { x: 0, y: 0 };
  private isSmallScreen: boolean = false;
  private isHoverMode: boolean | undefined = undefined;
  private orientation: string = '';

  primaryLayout: Layout = initLayout();
  secondaryLayout: Layout = initLayout();
  extraLayout: Layout = initLayout();
  extraOpacity: number = 1;

  updateOrientation = (orientation: string): void => {
    this.orientation = orientation;
  };

  updateContainerSize(size: Size): void {
    this.containerSize.width = size.width;
    this.containerSize.height = size.height;
  }

  constructor(props: FoldSplitContainerProps) {
    super(props);
    this.state = {
      primaryPosition: this.containerGlobalPosition,
      secondPosition: this.containerGlobalPosition,
      extraPosition: this.containerGlobalPosition,
      primarySize: this.containerSize,
      secondSize: this.containerSize,
      extraSize: this.containerSize,
    };
  }

  // 当传入参数更新时，延时更新布局，如果不延时则updateLayout()只能获取到旧布局数据
  shouldComponentUpdate(nextProps: Readonly<FoldSplitContainerProps>, nextState: Readonly<FoldSplitContainerState>, _nextContext): boolean {
    if (this.props !== nextProps && nextState === this.state) {
      setTimeout(() => {
        this.updateLayout();
      }, 10);
    }
    return true;
  }

  onLayout = (event): void => {
    const { nativeEvent } = event;
    const { layout } = nativeEvent;
    this.containerSize = { width: layout.width, height: layout.height };
    this.updateLayout();
  };

  componentDidMount(): void {
    this.foldStatus = Fold?.getFoldStatus() !== 'expanded' ? FoldStatus.FOLD_STATUS_FOLDED : FoldStatus.FOLD_STATUS_EXPANDED;
    Orientation.getOrientation((err, orientation) => {
      if (err) {
        this.orientation = 'PORTRAIT';
      } else {
        this.orientation = orientation;
      }
    });
    this.updatePreferredOrientation();
    const foldStatusString = ['unknown', 'expanded', 'folded', 'half-folded'];
    Fold?.addFoldListener(e => {
      this.foldStatus = foldStatusString.indexOf(e);
      this.updateLayout();
      this.updatePreferredOrientation();
    });
    Dimensions.addEventListener('change', ({ window: { width } }) => {
      this.isSmallScreen = width < SMALL_SCREEN_WIDTH;
    });
    Orientation.addOrientationListener(this.updateOrientation);
    this.updateLayout();
  }

  componentWillUnmount(): void {
    Orientation.removeOrientationListener(() => {});
    Fold?.removeFoldListener();
    Orientation.lockToPortrait();
  }

  updateLayout(): void {
    let isHoverMode: boolean = false;
    let regionLayout: RegionLayout;
    if (this.foldStatus === FoldStatus.FOLD_STATUS_FOLDED || this.isSmallScreen) {
      regionLayout = this.getFoldedRegionLayouts();
    } else {
      if (this.foldStatus === FoldStatus.FOLD_STATUS_EXPANDED) {
        regionLayout = this.getExpandedRegionLayouts();
      } else if (this.foldStatus === FoldStatus.FOLD_STATUS_HALF_FOLDED) {
        if (!this.shouldUseSplitViewInHover()) {
          regionLayout = this.getExpandedRegionLayouts();
        } else {
          regionLayout = this.getHoverModeRegionLayouts();
          isHoverMode = true;
        }
      } else {
        regionLayout = this.getExpandedRegionLayouts();
      }
    }

    this.primaryLayout = regionLayout.primary;
    this.secondaryLayout = regionLayout.secondary;
    this.extraLayout = regionLayout.extra;
    this.setState({
      primaryPosition: this.primaryLayout.position,
      primarySize: this.primaryLayout.size,
      secondPosition: this.secondaryLayout.position,
      secondSize: this.secondaryLayout.size,
      extraPosition: this.extraLayout.position,
      extraSize: this.extraLayout.size,
    });

    if (this.isHoverMode !== isHoverMode) {
      this.dispatchHoverStatusChange(isHoverMode);
      this.isHoverMode = isHoverMode;
    }

    if (
      this.isSmallScreen ||
      this.foldStatus === FoldStatus.FOLD_STATUS_FOLDED ||
      (isHoverMode && this.props.hoverModeLayoutOptions && !this.props.hoverModeLayoutOptions.showExtraRegion)
    ) {
      this.extraOpacity = 0;
    } else {
      this.extraOpacity = 1;
    }
  }

  private updatePreferredOrientation(): void {
    this.foldStatus === FoldStatus.FOLD_STATUS_FOLDED ? Orientation.lockToPortrait() : Orientation.unlockAllOrientations();
  }

  private getFoldedRegionLayouts(): RegionLayout {
    const width = this.containerSize.width;
    const height = this.containerSize.height;
    const primaryLayout: Layout = initLayout();
    const secondaryLayout: Layout = initLayout();
    const extraLayout: Layout = initLayout();

    const verticalSplitRatio =
      this.props.foldedLayoutOptions && this.props.foldedLayoutOptions.verticalSplitRatio > 0
        ? this.props.foldedLayoutOptions.verticalSplitRatio
        : PresetSplitRatio.LAYOUT_1V1;
    secondaryLayout.size.height = height / (verticalSplitRatio + 1);
    primaryLayout.size.height = height - secondaryLayout.size.height;
    extraLayout.size.height = 0;
    primaryLayout.size.width = width;
    secondaryLayout.size.width = width;
    extraLayout.size.width = 0;
    primaryLayout.position.x = 0;
    secondaryLayout.position.x = 0;
    extraLayout.position.x = width;
    primaryLayout.position.y = 0;
    secondaryLayout.position.y = primaryLayout.size.height;
    extraLayout.position.y = 0;

    return {
      primary: primaryLayout,
      secondary: secondaryLayout,
      extra: extraLayout,
    };
  }

  private getExpandedHorizontalSplitRatio(): number {
    return this.props.expandedLayoutOptions &&
      this.props.expandedLayoutOptions.horizontalSplitRatio &&
      this.props.expandedLayoutOptions.horizontalSplitRatio > 0
      ? this.props.expandedLayoutOptions.horizontalSplitRatio
      : PresetSplitRatio.LAYOUT_3V2;
  }

  private getExpandedVerticalSplitRatio(): number {
    return this.props.expandedLayoutOptions && this.props.expandedLayoutOptions.verticalSplitRatio && this.props.expandedLayoutOptions.verticalSplitRatio > 0
      ? this.props.expandedLayoutOptions.verticalSplitRatio
      : PresetSplitRatio.LAYOUT_1V1;
  }

  private getExpandedRegionLayouts(): RegionLayout {
    const width = this.containerSize.width;
    const height = this.containerSize.height;
    const primaryLayout: Layout = initLayout();
    const secondaryLayout: Layout = initLayout();
    const extraLayout: Layout = initLayout();

    const horizontalSplitRatio = this.getExpandedHorizontalSplitRatio();
    const verticalSplitRatio = this.getExpandedVerticalSplitRatio();
    extraLayout.size.width = this.props.extra ? width / (horizontalSplitRatio + 1) : 0;
    secondaryLayout.size.height = height / (verticalSplitRatio + 1);
    primaryLayout.size.height = height - secondaryLayout.size.height;
    primaryLayout.position.x = 0;
    secondaryLayout.position.x = 0;
    primaryLayout.position.y = 0;
    secondaryLayout.position.y = primaryLayout.size.height;

    const isExtraRegionPerpendicular = this.props.expandedLayoutOptions ? this.props.expandedLayoutOptions.isExtraRegionPerpendicular : true;

    if (isExtraRegionPerpendicular) {
      primaryLayout.size.width = width - extraLayout.size.width;
      secondaryLayout.size.width = width - extraLayout.size.width;
      extraLayout.size.height = height;
      extraLayout.position.x = primaryLayout.size.width;
      extraLayout.position.y = 0;
    } else {
      const extraRegionPosition = this.props.expandedLayoutOptions ? this.props.expandedLayoutOptions.extraRegionPosition : ExtraRegionPosition.TOP;
      if (extraRegionPosition === ExtraRegionPosition.BOTTOM) {
        primaryLayout.size.width = width;
        secondaryLayout.size.width = width - extraLayout.size.width;
        extraLayout.size.height = secondaryLayout.size.height;
        extraLayout.position.x = secondaryLayout.size.width;
        extraLayout.position.y = primaryLayout.size.height;
      } else {
        primaryLayout.size.width = width - extraLayout.size.width;
        secondaryLayout.size.width = width;
        extraLayout.size.height = primaryLayout.size.height;
        extraLayout.position.x = primaryLayout.size.width;
        extraLayout.position.y = 0;
      }
    }

    return {
      primary: primaryLayout,
      secondary: secondaryLayout,
      extra: extraLayout,
    };
  }

  private isPortraitOrientation(): boolean {
    return this.orientation === 'PORTRAIT';
  }

  private shouldUseSplitViewInHover(): boolean {
    if (this.isPortraitOrientation() && Fold.isHorizontalFold(this.isPortraitOrientation())) {
      return true;
    } else if (!this.isPortraitOrientation() && !Fold.isHorizontalFold(this.isPortraitOrientation())) {
      return true;
    } else {
      return false;
    }
  }

  private getHoverHorizontalSplitRatio(): number {
    return this.props.hoverModeLayoutOptions &&
      this.props.hoverModeLayoutOptions.horizontalSplitRatio &&
      this.props.hoverModeLayoutOptions.horizontalSplitRatio > 0
      ? this.props.hoverModeLayoutOptions.horizontalSplitRatio
      : PresetSplitRatio.LAYOUT_3V2;
  }

  private initializeLayouts(): RegionLayout {
    const primaryLayout: Layout = initLayout();
    const secondaryLayout: Layout = initLayout();
    const extraLayout: Layout = initLayout();
    return {
      primary: primaryLayout,
      secondary: secondaryLayout,
      extra: extraLayout,
    };
  }

  private handleFoldCreaseRegion(primaryLayout: Layout, secondaryLayout: Layout, height: number): void {
    const creaseRegionRects: number[][] = Fold?.getFoldCreaseRegion();
    if (creaseRegionRects.length > 0 && creaseRegionRects[0].length >= 4) {
      const creaseRegionRect = creaseRegionRects[0];
      primaryLayout.position.x = 0;
      primaryLayout.position.y = 0;
      secondaryLayout.position.x = 0;
      secondaryLayout.position.y = creaseRegionRect[1] + creaseRegionRect[3];
      secondaryLayout.size.height = height - secondaryLayout.position.y;
      primaryLayout.size.height = creaseRegionRect[1];
    }
  }

  private handleLayoutWithoutExtraRegion(primaryLayout: Layout, secondaryLayout: Layout, extraLayout: Layout, width: number): void {
    primaryLayout.size.width = width;
    secondaryLayout.size.width = width;
    extraLayout.position.x = width;
    const isExpandedExtraRegionPerpendicular = this.props.expandedLayoutOptions ? this.props.expandedLayoutOptions.isExtraRegionPerpendicular : true;

    if (isExpandedExtraRegionPerpendicular) {
      extraLayout.size.height = this.extraLayout.size.height;
    } else {
      const expandedExtraRegionPosition = this.props.expandedLayoutOptions ? this.props.expandedLayoutOptions.extraRegionPosition : ExtraRegionPosition.TOP;

      if (expandedExtraRegionPosition === ExtraRegionPosition.BOTTOM) {
        extraLayout.size.height = secondaryLayout.size.height;
        extraLayout.position.y = secondaryLayout.position.y;
      } else {
        extraLayout.size.height = primaryLayout.size.height;
        extraLayout.position.y = 0;
      }
    }
  }

  private getHoverModeRegionLayouts(): RegionLayout {
    const width = this.containerSize.width;
    const height = this.containerSize.height;
    const { primary, secondary, extra } = this.initializeLayouts();
    // 处理折叠区域
    this.handleFoldCreaseRegion(primary, secondary, height);

    const showExtraRegion = this.props.hoverModeLayoutOptions ? this.props.hoverModeLayoutOptions.showExtraRegion : false;

    if (!showExtraRegion) {
      this.handleLayoutWithoutExtraRegion(primary, secondary, extra, width);
    } else {
      const horizontalSplitRatio = this.getHoverHorizontalSplitRatio();
      const extraRegionPosition = this.props.hoverModeLayoutOptions ? this.props.hoverModeLayoutOptions.extraRegionPosition : ExtraRegionPosition.TOP;
      extra.size.width = this.props.extra ? width / (horizontalSplitRatio + 1) : 0;
      if (extraRegionPosition === ExtraRegionPosition.BOTTOM) {
        primary.size.width = width;
        secondary.size.width = width - extra.size.width;
        extra.size.height = secondary.size.height;
        extra.position.x = secondary.size.width;
        extra.position.y = secondary.position.y;
      } else {
        extra.size.height = primary.size.height;
        primary.size.width = width - extra.size.width;
        secondary.size.width = width;
        extra.position.x = primary.position.x + primary.size.width;
        extra.position.y = 0;
      }
    }

    return {
      primary: primary,
      secondary: secondary,
      extra: extra,
    };
  }

  private dispatchHoverStatusChange(isHoverMode: boolean): void {
    if (this.props.onHoverStatusChange) {
      this.props.onHoverStatusChange({
        foldStatus: this.foldStatus,
        isHoverMode: isHoverMode,
      });
    }
  }

  render(): JSX.Element {
    const { primary, secondary, extra } = this.props;
    const onLayout = this.onLayout;
    const { primarySize, secondSize, extraSize, primaryPosition, secondPosition, extraPosition } = this.state;
    return (
      <View style={styles.itemContainer} onLayout={onLayout}>
        <View
          style={{
            width: primarySize.width,
            height: primarySize.height,
            position: 'absolute',
            left: primaryPosition.x,
            top: primaryPosition.y,
          }}>
          {primary}
        </View>
        <View
          style={{
            width: secondSize.width,
            height: secondSize.height,
            position: 'absolute',
            left: secondPosition.x,
            top: secondPosition.y,
          }}>
          {secondary}
        </View>
        {extra && (
          <View
            style={{
              width: extraSize.width,
              height: extraSize.height,
              position: 'absolute',
              left: extraPosition.x,
              top: extraPosition.y,
              opacity: this.extraOpacity,
            }}>
            {extra}
          </View>
        )}
      </View>
    );
  }
}

const styles = StyleSheet.create({
  itemContainer: {
    flex: 1,
    flexDirection: 'column',
  },
});
